﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using TeaCompiler.CodeDom;

namespace TeaCompiler.Parser {

    /// <summary>
    /// 词法解析工具。
    /// </summary>
    public class Lexer {

        #region Internal

        TextReader _source;

        Location _currentLocation;

        int _currentChar;

        public TextReader Source {
            get {
                return _source;
            }
            set {
                _source = value;
                _currentLocation.Line = _currentLocation.Column = 1;

                // 预读一个字符。
                _currentChar = _source.Read();
            }

        }

        public Location CurrentLocation {
            get {
                return _currentLocation;
            }
            set {
                _currentLocation = value;
            }
        }

        ///// <summary>
        ///// 获取或设置是否启用严格模式。
        ///// </summary>
        //public bool IsStrictMode {
        //    get;
        //    set;
        //}

        public Lexer()
            : this(TextReader.Null) {
        }

        public Lexer(TextReader source) {
            Source = source;
        }

        #endregion

        #region Token

        const int UnusedTokenLength = 10;

        int _unusedTokensPointer = 0;

        /// <summary>
        /// 暂时未使用的标识符。
        /// </summary>
        Token[] _unusedTokens = new Token[UnusedTokenLength];

        Token _currentToken = new Token();

        /// <summary>
        /// 获取或设置当前词法分析器是否执行了 Read() 操作。
        /// </summary>
        public bool HasRead {
            get;
            set;
        }

        /// <summary>
        /// 获取当前的标识符。
        /// </summary>
        public Token Current {
            get {
                return _currentToken;
            }
        }

        /// <summary>
        /// 读取下一个标识符。
        /// </summary>
        /// <returns></returns>
        public Token Read() {

            HasRead = true;

            // 如果已经预览过下一个标识符，则直接使用。
            if (_currentToken.NextToken != null) {

                Token next = _currentToken.NextToken;

                // 缓存当前的 Token 重复利用。
                if (_unusedTokensPointer < UnusedTokenLength) {
                    _currentToken.HasLineTerminatorBeforeStart = false;
                    _currentToken.NextToken = null;
                    _unusedTokens[_unusedTokensPointer++] = _currentToken;
                }

                return _currentToken = next;
            }

            // 读取新的数据，保存到 _currentToken 。
            Scan(_currentToken);

            return _currentToken;
        }

        /// <summary>
        /// 预览下一个标识符。
        /// </summary>
        /// <returns></returns>
        public Token Peek() {

            // 如果已经预览过下一个标识符，则直接使用。
            if (_currentToken.NextToken != null) {
                return _currentToken.NextToken;
            }

            // 如果没有可用的标识符缓存，则新建一个。
            Token t = _unusedTokensPointer == 0 ? new Token() : _unusedTokens[--_unusedTokensPointer];

            _currentToken.NextToken = t;

            Scan(t);

            return t;
        }

        /// <summary>
        /// 预览下一个标识符。
        /// </summary>
        /// <returns></returns>
        public Token Peek(int distance) {

            Token t = _currentToken;

            while (distance-- >= 0) {

                // 如果已经预览过下一个标识符，则直接使用。
                if (t.NextToken == null) {

                    // 如果没有可用的标识符缓存，则新建一个。
                    t.NextToken = _unusedTokensPointer == 0 ? new Token() : _unusedTokens[--_unusedTokensPointer];

                    Scan(t.NextToken);

                }

                t = t.NextToken;

            }

            return t;
        }

        #endregion

        #region Scan

        /// <summary>
        /// 读取一下一个操作符的数据，并保存到指定的 Token 对象。
        /// </summary>
        /// <param name="currentToken"></param>
        void Scan(Token currentToken) {

            // 开始过滤空格。
        skip:

            // 读取下一个字符。
            switch (_currentChar) {

                case ' ':
                case '\t':
                    _currentChar = _source.Read();
                    _currentLocation.Column++;
                    goto skip;

                case '\r':
                    if (_source.Peek() == '\n') {
                        _source.Read();
                    }
                    goto case '\n';
                case '\n':
                    _currentChar = _source.Read();
                    NewLine(currentToken);
                    goto skip;

                default:

                    // 下一个标识符的开始位置是当前位置。
                    currentToken.StartLocation = _currentLocation;

                    if (TeaUnicode.IsLetter(_currentChar)) {
                        currentToken.Type = TokenType.Identifier;
                        currentToken.LiteralBuffer.Length = 0;
                        currentToken.LiteralBuffer.Append((char)_currentChar);
                        ScanIdentifierSuffix(currentToken);
                        _currentLocation.Column += currentToken.LiteralBuffer.Length;

                        #region Keywords

                        StringBuilder value = currentToken.LiteralBuffer;

                        switch (value.Length) {

                            #region 2

                            case 2:
                                switch (value[1]) {

                                    case 'f':
                                        if (value[0] == 'i')
                                            currentToken.Type = TokenType.If;
                                        break;

                                    case 'n':
                                        if (value[0] == 'i')
                                            currentToken.Type = TokenType.In;
                                        break;

                                    case 'o':
                                        switch (value[0]) {
                                            case 'd':
                                                currentToken.Type = TokenType.Do;
                                                break;
                                            case 't':
                                                currentToken.Type = TokenType.To;
                                                break;
                                        }
                                        break;

                                    case 's':
                                        switch (value[0]) {
                                            case 'a':
                                                currentToken.Type = TokenType.As;
                                                break;
                                            case 'i':
                                                currentToken.Type = TokenType.Is;
                                                break;
                                        }
                                        break;

                                    case 'r':
                                        if (value[0] == 'o')
                                            currentToken.Type = TokenType.OrReturn;
                                        break;

                                }

                                break;

                            #endregion

                            #region 3

                            case 3:
                                switch (value[0]) {

                                    case 'f':
                                        if (value[1] == 'o' && value[2] == 'r')
                                            currentToken.Type = TokenType.For;
                                        break;

                                    //case 'i':
                                    //    if (value[1] == 'n' && value[2] == 't') return TokenType.Int;
                                    //    break;

                                    case 'l':
                                        if (value[1] == 'e' && value[2] == 't')
                                            currentToken.Type = TokenType.Let;
                                        break;

                                    case 'n':
                                        if (value[1] == 'e' && value[2] == 'w')
                                            currentToken.Type = TokenType.New;
                                        break;

                                    case 't':
                                        if (value[1] == 'r' && value[2] == 'y')
                                            currentToken.Type = TokenType.Try;
                                        break;

                                    case 'v':
                                        if (value[1] == 'a' && value[2] == 'r')
                                            currentToken.Type = TokenType.Var;
                                        break;

                                    case 'r':
                                        if (value[1] == 'e' && value[2] == 'f')
                                            currentToken.Type = TokenType.Ref;
                                        break;

                                }

                                break;

                            #endregion

                            #region 4

                            case 4:


                                switch (value[0]) {
                                    //case 'b':
                                    //    if (value[1] == 'y' && value[2] == 't' && value[3] == 'e')
                                    //        return TokenType.Byte;
                                    //    break;
                                    case 'b':
                                        if (value[1] == 'a' && value[2] == 's' && value[3] == 'e')
                                            currentToken.Type = TokenType.Base;
                                        break;

                                    case 'c':

                                        switch (value[3]) {
                                            case 'e':
                                                if (value[2] == 's' && value[1] == 'a')
                                                    currentToken.Type = TokenType.Case;
                                                break;

                                            //case 'r':
                                            //    if (value[2] == 's' && value[1] == 'h')
                                            //        return TokenType.Char;
                                            //    break;

                                        }


                                        break;
                                    case 'e':

                                        switch (value[3]) {
                                            case 'e':
                                                if (value[2] == 's' && value[1] == 'l')
                                                    currentToken.Type = TokenType.Else;
                                                break;
                                            case 'm':
                                                if (value[2] == 'u' && value[1] == 'n')
                                                    currentToken.Type = TokenType.Enum;
                                                break;

                                        }

                                        break;

                                    case 'g':
                                        if (value[1] == 'o' && value[2] == 't' && value[3] == 'o')
                                            currentToken.Type = TokenType.Goto;
                                        break;

                                    case 'l':
                                        switch (value[3]) {
                                            case 'k':
                                                if (value[1] == 'o' && value[2] == 'c')
                                                    currentToken.Type = TokenType.Lock;
                                                break;
                                            case 'p':
                                                if (value[1] == 'o' && value[2] == 'o')
                                                    currentToken.Type = TokenType.Loop;
                                                break;
                                        }
                                        break;
                                    //    if (value[1] == 'o' && value[2] == 'n' && value[3] == 'g')
                                    //        return TokenType.Long;
                                    //    break;

                                    case 'n':
                                        if (value[1] == 'u' && value[2] == 'l' && value[3] == 'l')
                                            currentToken.Type = TokenType.NullLiteral;
                                        break;

                                    case 't':

                                        switch (value[3]) {
                                            case 'e':
                                                if (value[2] == 'u' && value[1] == 'r')
                                                    currentToken.Type = TokenType.TrueLiteral;
                                                break;

                                            case 's':
                                                if (value[2] == 'i' && value[1] == 'h')
                                                    currentToken.Type = TokenType.This;
                                                break;

                                        }

                                        break;

                                }

                                break;

                            #endregion

                            #region 5

                            case 5:
                                switch (value[2]) {
                                    case 'a':
                                        switch (value[0]) {
                                            case 'c':
                                                if (value[1] == 'l' && value[3] == 's' && value[4] == 's') {
                                                    currentToken.Type = TokenType.Class;
                                                }
                                                break;
                                            case 't':
                                                if (value[1] == 'r' && value[3] == 'c' && value[4] == 'e') {
                                                    currentToken.Type = TokenType.Trace;
                                                }
                                                break;
                                            case 'a':
                                                if (value[1] == 'w' && value[3] == 'i' && value[4] == 't')
                                                    currentToken.Type = TokenType.Await;

                                                break;
                                        }
                                        break;
                                    case 'e':
                                        switch (value[0]) {
                                            case 'e':
                                                if (value[1] == 'v' && value[3] == 'n' && value[4] == 't')
                                                    currentToken.Type = TokenType.Event;

                                                break;
                                            case 'b':
                                                if (value[1] == 'r' && value[3] == 'a' && value[4] == 'k')
                                                    currentToken.Type = TokenType.Break;

                                                break;

                                        }
                                        break;
                                    case 'i':
                                        if (value[0] == 'w' && value[1] == 'h' && value[3] == 'l' && value[4] == 'e')
                                            currentToken.Type = TokenType.While;

                                        break;
                                    case 'l':
                                        if (value[0] == 'f' && value[1] == 'a' && value[3] == 's' && value[4] == 'e')
                                            currentToken.Type = TokenType.FalseLiteral;

                                        break;
                                    case 'n':
                                        switch (value[0]) {
                                            case 'c':
                                                if (value[1] == 'o' && value[3] == 's' && value[4] == 't')
                                                    currentToken.Type = TokenType.Const;

                                                break;

                                            case 'f':
                                                if (value[1] == 'i' && value[3] == 'a' && value[4] == 'l')
                                                    currentToken.Type = TokenType.Final;

                                                break;
                                        }


                                        break;
                                    //case 'o':
                                    //    switch (value[0]) {
                                    //        case 'f':
                                    //            if (value[1] == 'l' && value[3] == 'a' && value[4] == 't')
                                    //                return TokenType.Float;

                                    //            break;

                                    //        case 's':
                                    //            if (value[1] == 'h' && value[3] == 'r' && value[4] == 't')
                                    //                return TokenType.Short;

                                    //            break;
                                    //    }


                                    //    break;
                                    case 'r':
                                        if (value[0] == 't' && value[1] == 'h' && value[3] == 'o' && value[4] == 'w')
                                            currentToken.Type = TokenType.Throw;

                                        break;
                                    case 't':
                                        if (value[0] == 'c' && value[1] == 'a' && value[3] == 'c' && value[4] == 'h')
                                            currentToken.Type = TokenType.Catch;

                                        break;
                                }

                                break;

                            #endregion

                            #region 6

                            case 6:

                                switch (value[1]) {
                                    case 'a':
                                        switch (value[0]) {
                                            case 'n':
                                                if (value[2] == 't' && value[3] == 'i' && value[4] == 'v' && value[5] == 'e')
                                                    currentToken.Type = TokenType.Native;
                                                break;
                                            //case 'p':
                                            //    if (value[2] == 'r' && value[3] == 'a' && value[4] == 'm' && value[5] == 's')
                                            //        currentToken.Type = TokenType.Params;
                                            //    break;

                                        }


                                        break;
                                    case 'e':
                                        switch (value[0]) {
                                            case 'd':
                                                if (value[2] == 'l' && value[3] == 'e' && value[4] == 't' && value[5] == 'e')
                                                    currentToken.Type = TokenType.Delete;

                                                break;

                                            case 'r':
                                                if (value[2] == 't' && value[3] == 'u' && value[4] == 'r' && value[5] == 'n')
                                                    currentToken.Type = TokenType.Return;

                                                break;
                                        }

                                        break;
                                    case 'm':
                                        if (value[0] == 'i' && value[2] == 'p' && value[3] == 'o' && value[4] == 'r' && value[5] == 't')
                                            currentToken.Type = TokenType.Import;

                                        break;
                                    //case 'o':
                                    //    if (value[0] == 'd' && value[2] == 'u' && value[3] == 'b' && value[4] == 'l' && value[5] == 'e')
                                    //        return TokenType.Double;
                                    //    break;

                                    case 't':
                                        if (value[0] == 's' && value[2] == 'a' && value[3] == 't' && value[4] == 'i' && value[5] == 'c')
                                            currentToken.Type = TokenType.Static;
                                        break;

                                    case 'u':
                                        if (value[0] == 'p' && value[2] == 'b' && value[3] == 'l' && value[4] == 'i' && value[5] == 'c')
                                            currentToken.Type = TokenType.Public;
                                        break;

                                    case 'w':
                                        if (value[0] == 's' && value[2] == 'i' && value[3] == 't' && value[4] == 'c' && value[5] == 'h')
                                            currentToken.Type = TokenType.Switch;
                                        break;

                                    case 's':
                                        if (value[0] == 'a' && value[2] == 's' && value[3] == 'e' && value[4] == 'r' && value[5] == 't')
                                            currentToken.Type = TokenType.Assert;
                                        break;

                                    //case 'x':
                                    //    if (value[0] == 'e' && value[2] == 'p' && value[3] == 'o' && value[4] == 'r' && value[5] == 't')
                                    //        return TokenType.Export;
                                    //    break;

                                    case 'y':
                                        if (value[0] == 't' && value[2] == 'p' && value[3] == 'e' && value[4] == 'o' && value[5] == 'f')
                                            currentToken.Type = TokenType.Typeof;
                                        break;

                                }

                                break;

                            #endregion

                            #region 7

                            case 7:
                                switch (value[1]) {
                                    //case 'a':
                                    //    if (value[0] == 'p' && value[2] == 'c' && value[3] == 'k' && value[4] == 'a' && value[5] == 'g' && value[6] == 'e')
                                    //        return TokenType.Package;
                                    //    break;

                                    case 'e':
                                        if (value[0] == 'd' && value[2] == 'f' && value[3] == 'a' && value[4] == 'u' && value[5] == 'l' && value[6] == 't')
                                            currentToken.Type = TokenType.Default;
                                        break;

                                    case 'i':
                                        switch (value[0]) {
                                            case 'f':
                                                if (value[2] == 'n' && value[3] == 'a' && value[4] == 'l' && value[5] == 'l' && value[6] == 'y')
                                                    currentToken.Type = TokenType.Finally;
                                                break;
                                            case 'v':
                                                if (value[2] == 'r' && value[3] == 't' && value[4] == 'u' && value[5] == 'a' && value[6] == 'l')
                                                    currentToken.Type = TokenType.Virtual;
                                                break;
                                        }
                                        break;

                                    //case 'o':
                                    //    if (value[0] == 'b' && value[2] == 'o' && value[3] == 'l' && value[4] == 'a' && value[5] == 'a' && value[6] == 'n') return TokenType.Boolean;
                                    //    break;

                                    case 'r':
                                        if (value[0] == 'p' && value[2] == 'i' && value[3] == 'v' && value[4] == 'a' && value[5] == 't' && value[6] == 'e')
                                            currentToken.Type = TokenType.Private;
                                        break;

                                    case 'x':
                                        if (value[0] == 'e' && value[2] == 't' && value[3] == 'e' && value[4] == 'n' && value[5] == 'd' && value[6] == 's')
                                            currentToken.Type = TokenType.Extends;
                                        break;

                                }

                                break;

                            #endregion

                            #region 8

                            case 8:

                                switch (value[0]) {

                                    case 'a':
                                        if (value[1] == 'b' && value[2] == 's' && value[3] == 't' && value[4] == 'r' && value[5] == 'a' && value[6] == 'c' && value[7] == 't')
                                            currentToken.Type = TokenType.Abstract;
                                        break;

                                    case 'i':
                                        if (value[1] == 'n' && value[2] == 't' && value[3] == 'e' && value[4] == 'r' && value[5] == 'a' && value[6] == 'n' && value[7] == 'l')
                                            currentToken.Type = TokenType.Internal;
                                        break;

                                    case 'c':
                                        if (value[1] == 'o' && value[2] == 'n' && value[3] == 't' && value[4] == 'i' && value[5] == 'n' && value[6] == 'u' && value[7] == 'e')
                                            currentToken.Type = TokenType.Continue;
                                        break;

                                    case 'd':
                                        if (value[1] == 'e' && value[2] == 'b' && value[3] == 'u' && value[4] == 'g' && value[5] == 'g' && value[6] == 'e' && value[7] == 'r')
                                            currentToken.Type = TokenType.Debugger;
                                        break;

                                    case 'o':
                                        switch (value[1]) {
                                            case 'v':
                                                if (value[2] == 'e' && value[3] == 'r' && value[4] == 'r' && value[5] == 'i' && value[6] == 'd' && value[7] == 'e')
                                                    currentToken.Type = TokenType.Override;
                                                break;
                                            case 'p':
                                                if (value[2] == 'e' && value[3] == 'r' && value[4] == 'a' && value[5] == 't' && value[6] == 'o' && value[7] == 'r')
                                                    currentToken.Type = TokenType.Operator;
                                                break;
                                        }
                                        break;

                                    case 'f':
                                        if (value[1] == 'u' && value[2] == 'n' && value[3] == 'c' && value[4] == 't' && value[5] == 'i' && value[6] == 'o' && value[7] == 'n')
                                            currentToken.Type = TokenType.Function;
                                        break;


                                    case 'v':
                                        if (value[1] == 'o' && value[2] == 'l' && value[3] == 'a' && value[4] == 't' && value[5] == 'i' && value[6] == 'l' && value[7] == 'e')
                                            currentToken.Type = TokenType.Volatile;
                                        break;



                                }

                                break;

                            #endregion

                            #region 9

                            case 9:

                                switch (value[0]) {
                                    case 'i':
                                        if (value[1] == 'n' && value[2] == 't' && value[3] == 'e' && value[4] == 'r' && value[5] == 'f' && value[6] == 'a' && value[7] == 'c' && value[8] == 'e')
                                            currentToken.Type = TokenType.Interface;
                                        break;

                                    case 'p':
                                        if (value[1] == 'r' && value[2] == 'o' && value[3] == 't' && value[4] == 'e' && value[5] == 'c' && value[6] == 't' && value[7] == 'e' && value[8] == 'd')
                                            currentToken.Type = TokenType.Protected;
                                        break;

                                }
                                break;

                            #endregion

                        }

                        #endregion

                    } else if (TeaUnicode.IsDecimalDigit(_currentChar)) {

                        #region Numbers

                        currentToken.Type = TokenType.IntLiteral;
                        currentToken.LiteralBuffer.Length = 0;

                        if (_currentChar == '0') {
                            int peek = _source.Peek();
                            if (peek == 'x' || peek == 'X') {
                                currentToken.Type = TokenType.HexLiteral;
                                _source.Read();
                                _currentLocation.Column += 2;
                                while (TeaUnicode.IsHexDigit(_currentChar = _source.Read())) {
                                    currentToken.LiteralBuffer.Append((char)_currentChar);
                                }

                                if (currentToken.LiteralBuffer.Length == 0) {
                                    currentToken.Type = TokenType.Illegal;
                                    currentToken.LiteralBuffer.Append("BadNumber");
                                }

                                goto endreaddecemial;
                            }
                        }

                        AddDecimalDigitsToLiteralBuffer(currentToken);

                        switch (_currentChar) {
                            case ' ':
                                break;
                            case '.':
                                if (TeaUnicode.IsDecimalDigit(_source.Peek())) {
                                    _currentChar = _source.Read();
                                    ScanRealNumber(currentToken, true);
                                }
                                break;
                            case 'e':
                            case 'E':
                                ScanRealNumber(currentToken, false);
                                break;
                            case 'l':
                            case 'L':
                                currentToken.Type = TokenType.LongLiteral;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;

                                if (_currentChar == 'u' || _currentChar == 'U') {
                                    currentToken.Type = TokenType.ULongLiteral;
                                    _currentChar = _source.Read();
                                    _currentLocation.Column++;
                                }

                                break;
                            case 'u':
                            case 'U':
                                currentToken.Type = TokenType.UIntLiteral;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;

                                if (_currentChar == 'l' || _currentChar == 'L') {
                                    currentToken.Type = TokenType.ULongLiteral;
                                    _currentChar = _source.Read();
                                    _currentLocation.Column++;
                                }
                                break;
                            case 'D':
                            case 'd':
                                currentToken.Type = TokenType.DoubleLiteral;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;
                                break;
                            case 'F':
                            case 'f':
                                currentToken.Type = TokenType.FloatLiteral;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;
                                break;
                            case 'M':
                            case 'm':
                                currentToken.Type = TokenType.DecimalLiteral;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;
                                break;
                        }

                    endreaddecemial:

                        _currentLocation.Column += currentToken.LiteralBuffer.Length;

                        if (TeaUnicode.IsIdentifierPart(_currentChar)) {
                            currentToken.Type = TokenType.Illegal;
                            currentToken.LiteralBuffer.Insert(0, "Illegal Character after ");
                        }

                        #endregion

                    } else {

                        #region Operators

                        switch (_currentChar) {

                            case '\'':
                            case '"': {

                                    #region Strings

                                    currentToken.Type = TokenType.StringLiteral;
                                    currentToken.LiteralBuffer.Length = 0;

                                    int quote = _currentChar;
                                    bool hasTemplateChar = false;

                                parsenext1:
                                    _currentLocation.Column++;
                                    switch (_currentChar = _source.Read()) {

                                        // 处理转义。
                                        case '\\':
                                            // 判断是否下一个字符是 EOF 。
                                            _currentLocation.Column++;
                                            switch (_currentChar = _source.Read()) {
                                                case -1:
                                                    currentToken.LiteralBuffer.Append('\\');
                                                    break;
                                                case '\r':
                                                    if (_source.Peek() == '\n') {
                                                        _source.Read();
                                                    }
                                                    goto case '\n';
                                                case '\n':
                                                    NewLine(currentToken);
                                                    break;
                                                case 'n':
                                                    _currentChar = '\n';
                                                    goto default;
                                                case 'r':
                                                    _currentChar = '\r';
                                                    goto default;
                                                case 't':
                                                    _currentChar = '\t';
                                                    goto default;
                                                case 'u':
                                                case 'U':
                                                    _currentChar = ScanHexEscape(4, _currentChar);
                                                    goto default;
                                                case '\'':
                                                case '"':
                                                case '\\':
                                                case '$':
                                                    goto default;
                                                case 'b':
                                                    _currentChar = '\b';
                                                    goto default;
                                                case 'f':
                                                    _currentChar = '\f';
                                                    goto default;
                                                case 'v':
                                                    _currentChar = '\v';
                                                    goto default;
                                                case '0':
                                                    _currentChar = '\0';
                                                    goto default;
                                                case 'x':
                                                case 'X':
                                                    _currentChar = ScanHexEscape(2, _currentChar);
                                                    goto default;
                                                default:
                                                    currentToken.LiteralBuffer.Append((char)_currentChar);
                                                    goto parsenext1;
                                            }
                                            break;

                                        case -1:
                                            break;

                                        case '\r':
                                            if (_source.Peek() == '\n') {
                                                _source.Read();
                                            }
                                            goto case '\n';
                                        case '\n':
                                            NewLine(currentToken);
                                            currentToken.LiteralBuffer.Append('\n');
                                            goto parsenext1;

                                        case '$':
                                            hasTemplateChar = true;
                                            currentToken.LiteralBuffer.Append('$');
                                            goto parsenext1;

                                        default:
                                            if (_currentChar == quote) {

                                                // 预读下一个字符。
                                                _currentChar = _source.Read();
                                                _currentLocation.Column++;
                                                break;
                                            }

                                            currentToken.LiteralBuffer.Append((char)_currentChar);

                                            goto parsenext1;

                                    }

                                    currentToken.Type = quote == '\"' ? (hasTemplateChar ? TokenType.TemplateStringLiteral : TokenType.StringLiteral) : (hasTemplateChar ? TokenType.TemplateCharLiteral : TokenType.CharLiteral);

                                    #endregion

                                }
                                break;

                            case '<':
                                // < <= << <<=
                                switch (_currentChar = _source.Read()) {
                                    case '=':
                                        currentToken.Type = TokenType.Lte;
                                        _currentLocation.Column += 2;
                                        _currentChar = _source.Read();
                                        break;
                                    case '<':
                                        switch (_currentChar = _source.Read()) {
                                            case '=':
                                                currentToken.Type = TokenType.AssignShl;
                                                _currentLocation.Column += 3;
                                                _currentChar = _source.Read();
                                                break;
                                            default:
                                                currentToken.Type = TokenType.Shl;
                                                _currentLocation.Column += 2;
                                                break;
                                        }
                                        break;
                                    default:
                                        currentToken.Type = TokenType.Lt;
                                        _currentLocation.Column++;
                                        break;
                                }
                                break;

                            case '>':
                                // > >= >> >>= >>>
                                switch (_currentChar = _source.Read()) {
                                    case '=':
                                        currentToken.Type = TokenType.Gte;
                                        _currentLocation.Column += 2;
                                        _currentChar = _source.Read();
                                        break;
                                    case '>':
                                        // >> >>=
                                        switch (_currentChar = _source.Read()) {
                                            case '=':
                                                currentToken.Type = TokenType.AssignShr;
                                                _currentLocation.Column += 3;
                                                _currentChar = _source.Read();
                                                break;
                                            //case '>':
                                            //    _nextToken.Type = TokenType.Shr;
                                            //    _currentLocation.Column += 3;
                                            //    _currentChar = _source.Read();
                                            //    break;
                                            default:
                                                currentToken.Type = TokenType.Shr;
                                                _currentLocation.Column += 2;
                                                break;
                                        }
                                        break;
                                    default:
                                        currentToken.Type = TokenType.Gt;
                                        _currentLocation.Column++;
                                        break;
                                }
                                break;

                            case '=':
                                // = == =>
                                switch (_currentChar = _source.Read()) {
                                    case '=':
                                        currentToken.Type = TokenType.Eq;
                                        _currentLocation.Column += 2;
                                        _currentChar = _source.Read();
                                        break;
                                    case '>':
                                        currentToken.Type = TokenType.Lambda;
                                        _currentLocation.Column += 2;
                                        _currentChar = _source.Read();
                                        break;
                                    default:
                                        currentToken.Type = TokenType.Assign;
                                        _currentLocation.Column++;
                                        break;
                                }
                                break;

                            case '!':
                                // ! !=
                                switch (_currentChar = _source.Read()) {
                                    case '=':
                                        currentToken.Type = TokenType.Ne;
                                        _currentLocation.Column += 2;
                                        _currentChar = _source.Read();
                                        break;
                                    default:
                                        currentToken.Type = TokenType.Not;
                                        _currentLocation.Column++;
                                        break;
                                }
                                break;

                            case '+':
                                // + ++ +=
                                switch (_currentChar = _source.Read()) {
                                    case '+':
                                        currentToken.Type = TokenType.Inc;
                                        _currentLocation.Column += 2;
                                        _currentChar = _source.Read();
                                        break;
                                    case '=':
                                        currentToken.Type = TokenType.AssignAdd;
                                        _currentLocation.Column += 2;
                                        _currentChar = _source.Read();
                                        break;
                                    default:
                                        currentToken.Type = TokenType.Add;
                                        _currentLocation.Column++;
                                        break;
                                }
                                break;

                            case '-':
                                // - -- -> -=
                                switch (_currentChar = _source.Read()) {
                                    case '-':
                                        currentToken.Type = TokenType.Dec;
                                        _currentLocation.Column += 2;
                                        _currentChar = _source.Read();
                                        break;
                                    case '=':
                                        currentToken.Type = TokenType.AssignSub;
                                        _currentLocation.Column += 2;
                                        _currentChar = _source.Read();
                                        break;
                                    case '>':
                                        currentToken.Type = TokenType.Pointer;
                                        _currentLocation.Column += 2;
                                        _currentChar = _source.Read();
                                        break;
                                    default:
                                        currentToken.Type = TokenType.Sub;
                                        _currentLocation.Column++;
                                        break;
                                }
                                break;

                            case '*':
                                // * *=
                                switch (_currentChar = _source.Read()) {
                                    case '=':
                                        currentToken.Type = TokenType.AssignMul;
                                        _currentLocation.Column += 2;
                                        _currentChar = _source.Read();
                                        break;
                                    default:
                                        currentToken.Type = TokenType.Mul;
                                        _currentLocation.Column++;
                                        break;
                                }
                                break;

                            case '%':
                                // % %=
                                switch (_currentChar = _source.Read()) {
                                    case '=':
                                        currentToken.Type = TokenType.AssignMod;
                                        _currentLocation.Column += 2;
                                        _currentChar = _source.Read();
                                        break;
                                    default:
                                        currentToken.Type = TokenType.Mod;
                                        _currentLocation.Column++;
                                        break;
                                }
                                break;

                            case '/':
                                // /  // /* /=
                                _currentLocation.Column += 2;
                                switch (_currentChar = _source.Read()) {
                                    case '/':

                                        // 忽略注释。
                                        while (TeaUnicode.IsNonTerminator(_currentChar = _source.Read())) {
                                            _currentLocation.Column++;
                                        }

                                        goto skip;
                                    //_nextToken.LiteralBuffer.Length = 0;
                                    //_currentLocation.Column += 2;
                                    //switch (_currentChar = _source.Read()) {
                                    //    case '/':
                                    //        _nextToken.Type = TokenType.SingleLineDocComment;
                                    //        _currentLocation.Column++;

                                    //        while (TeaUnicode.IsNonTerminator(_currentChar = _source.Read())) {
                                    //            _nextToken.LiteralBuffer.Append((char)_currentChar);
                                    //        }

                                    //        _currentLocation.Column += _nextToken.LiteralBuffer.Length;

                                    //        break;
                                    //    default:
                                    //        _nextToken.Type = TokenType.SingleLineComment;
                                    //        _nextToken.LiteralBuffer.Append((char)_currentChar);
                                    //        while (TeaUnicode.IsNonTerminator(_currentChar = _source.Read())) {
                                    //            _nextToken.LiteralBuffer.Append((char)_currentChar);
                                    //        }
                                    //        _currentLocation.Column += _nextToken.LiteralBuffer.Length;
                                    //        break;
                                    //}
                                    //break;
                                    case '*':

                                    parsenext2:
                                        switch (_currentChar = _source.Read()) {
                                            case '*':
                                                if (_source.Peek() == '/') {
                                                    _source.Read();
                                                    _currentChar = _source.Read();
                                                    _currentLocation.Column += 2;
                                                    break;
                                                }
                                                goto default;
                                            case '\r':
                                                if (_source.Peek() == '\n') {
                                                    _source.Read();
                                                }
                                                goto case '\n';
                                            case '\n':
                                                NewLine(currentToken);
                                                goto parsenext2;
                                            case -1:
                                                break;
                                            default:
                                                _currentLocation.Column++;
                                                goto parsenext2;
                                        }

                                        goto skip;



                                    //_nextToken.LiteralBuffer.Length = 0;
                                    //_currentLocation.Column += 2;
                                    //switch (_currentChar = _source.Read()) {
                                    //    case '*':
                                    //        if (_source.Peek() != '/') {

                                    //            _nextToken.Type = TokenType.MultiLineDocComment;
                                    //            _currentChar = _source.Read();
                                    //            _currentLocation.Column++;

                                    //        readdoccomment:
                                    //            switch (_currentChar) {
                                    //                case '*':
                                    //                    if (_source.Peek() == '/') {
                                    //                        _source.Read();
                                    //                        _currentChar = _source.Read();
                                    //                        _currentLocation.Column += 2;
                                    //                        break;
                                    //                    }
                                    //                    goto default;
                                    //                case '\r':
                                    //                    if (_source.Peek() == '\n') {
                                    //                        _source.Read();
                                    //                    }
                                    //                    goto case '\n';
                                    //                case '\n':
                                    //                    _nextToken.LiteralBuffer.Append('\n');
                                    //                    NewLine();

                                    //                    // 忽略空格。
                                    //                    while (TeaUnicode.IsWhiteSpace(_currentChar = _source.Read())) {
                                    //                        _currentLocation.Column++;
                                    //                    }

                                    //                    if (_currentChar == '*' && _source.Peek() != '/') {
                                    //                        _currentChar = _source.Read();
                                    //                        _currentLocation.Column++;

                                    //                    }

                                    //                    goto readdoccomment;
                                    //                case -1:
                                    //                    break;
                                    //                default:
                                    //                    _nextToken.LiteralBuffer.Append((char)_currentChar);
                                    //                    _currentChar = _source.Read();
                                    //                    _currentLocation.Column++;
                                    //                    goto readdoccomment;
                                    //            }
                                    //            break;
                                    //        } else {
                                    //            goto default;
                                    //        }

                                    //    default:
                                    //        _nextToken.Type = TokenType.MultiLineComment;

                                    //    readmorecomment:
                                    //        switch (_currentChar) {
                                    //            case '*':
                                    //                if (_source.Peek() == '/') {
                                    //                    _source.Read();
                                    //                    _currentChar = _source.Read();
                                    //                    _currentLocation.Column += 2;
                                    //                    break;
                                    //                }
                                    //                goto default;
                                    //            case '\r':
                                    //                if (_source.Peek() == '\n') {
                                    //                    _source.Read();
                                    //                }
                                    //                goto case '\n';
                                    //            case '\n':
                                    //                _nextToken.LiteralBuffer.Append('\n');
                                    //                NewLine();
                                    //                _currentChar = _source.Read();
                                    //                goto readmorecomment;
                                    //            case -1:
                                    //                break;
                                    //            default:
                                    //                _nextToken.LiteralBuffer.Append((char)_currentChar);
                                    //                _currentChar = _source.Read();
                                    //                _currentLocation.Column++;
                                    //                goto readmorecomment;
                                    //        }
                                    //        break;

                                    //}
                                    //break;
                                    case '=':
                                        currentToken.Type = TokenType.AssignDiv;
                                        _currentChar = _source.Read();
                                        break;
                                    default:
                                        currentToken.Type = TokenType.Div;
                                        _currentLocation.Column--;
                                        break;
                                }
                                break;

                            case '&':
                                // & && &=
                                _currentLocation.Column += 2;
                                switch (_currentChar = _source.Read()) {
                                    case '&':
                                        currentToken.Type = TokenType.And;
                                        _currentChar = _source.Read();
                                        break;
                                    case '=':
                                        currentToken.Type = TokenType.AssignBitAnd;
                                        _currentChar = _source.Read();
                                        break;
                                    default:
                                        currentToken.Type = TokenType.BitAnd;
                                        _currentLocation.Column--;
                                        break;
                                }
                                break;

                            case '|':
                                // | || |=
                                _currentLocation.Column += 2;
                                switch (_currentChar = _source.Read()) {
                                    case '|':
                                        currentToken.Type = TokenType.Or;
                                        _currentChar = _source.Read();
                                        break;
                                    case '=':
                                        currentToken.Type = TokenType.AssignBitOr;
                                        _currentChar = _source.Read();
                                        break;
                                    default:
                                        currentToken.Type = TokenType.BitOr;
                                        _currentLocation.Column--;
                                        break;
                                }
                                break;

                            case '.':
                                // . Number ..
                                _currentChar = _source.Read();
                                if (TeaUnicode.IsDecimalDigit(_currentChar)) {
                                    currentToken.LiteralBuffer.Length = 0;
                                    ScanRealNumber(currentToken, true);
                                    _currentLocation.Column += currentToken.LiteralBuffer.Length;
                                } else if (_currentChar == '.') {
                                    currentToken.Type = TokenType.PeriodChain;
                                    _currentChar = _source.Read();
                                    _currentLocation.Column += 2;
                                } else {
                                    currentToken.Type = TokenType.Period;
                                    _currentLocation.Column++;
                                }
                                break;

                            case ':':
                                // : := :=:
                                switch (_currentChar = _source.Read()) {
                                    case '=':
                                        switch (_currentChar = _source.Read()) {
                                            case ':':
                                                currentToken.Type = TokenType.AssignAll;
                                                _currentChar = _source.Read();
                                                _currentLocation.Column += 3;
                                                break;
                                            default:
                                                currentToken.Type = TokenType.AssignCopy;
                                                _currentLocation.Column += 2;
                                                break;
                                        }
                                        break;
                                    default:
                                        currentToken.Type = TokenType.Colon;
                                        _currentLocation.Column++;
                                        break;
                                }
                                break;

                            case ';':
                                currentToken.Type = TokenType.Semicolon;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;
                                break;

                            case ',':
                                currentToken.Type = TokenType.Comma;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;
                                break;

                            case '(':
                                currentToken.Type = TokenType.LParam;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;
                                break;

                            case ')':
                                currentToken.Type = TokenType.RParam;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;
                                break;

                            case '[':
                                currentToken.Type = TokenType.LBrack;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;
                                break;

                            case ']':
                                currentToken.Type = TokenType.RBrack;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;
                                break;

                            case '{':
                                currentToken.Type = TokenType.LBrace;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;
                                break;

                            case '}':
                                currentToken.Type = TokenType.RBrace;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;
                                break;

                            case '?':
                                // ? ??
                                switch (_currentChar = _source.Read()) {
                                    case '?':
                                        currentToken.Type = TokenType.CheckNull;
                                        _currentChar = _source.Read();
                                        _currentLocation.Column += 2;
                                        break;
                                    default:
                                        currentToken.Type = TokenType.Conditional;
                                        _currentLocation.Column++;
                                        break;
                                }
                                break;

                            case '~':
                                currentToken.Type = TokenType.BitNot;
                                _currentChar = _source.Read();
                                _currentLocation.Column++;
                                break;

                            case '\\':
                                currentToken.Type = TokenType.Identifier;
                                currentToken.LiteralBuffer.Length = 0;
                                int c = ScanIdentifierUnicodeEscape();
                                if (!TeaUnicode.IsIdentifierPart(c)) {
                                    currentToken.Type = TokenType.Illegal;
                                    currentToken.LiteralBuffer.Length = 0;
                                    currentToken.LiteralBuffer.AppendFormat("Bad IdentifierPart Character: {0}", (char)_currentChar);
                                    break;
                                }
                                currentToken.LiteralBuffer.Append((char)_currentChar);
                                _currentLocation.Column += currentToken.LiteralBuffer.Length;
                                break;

                            case '$':
                                currentToken.Type = TokenType.Identifier;
                                currentToken.LiteralBuffer.Length = 0;
                                ScanIdentifierSuffix(currentToken);
                                _currentLocation.Column += currentToken.LiteralBuffer.Length + 1;
                                break;

                            case '_':
                                currentToken.Type = TokenType.Identifier;
                                currentToken.LiteralBuffer.Length = 0;
                                currentToken.LiteralBuffer.Append((char)_currentChar);
                                ScanIdentifierSuffix(currentToken);
                                _currentLocation.Column += currentToken.LiteralBuffer.Length;
                                break;

                            case -1:
                                currentToken.Type = TokenType.EOF;
                                break;

                            case '`':
                                currentToken.Type = TokenType.DataLiteral;
                                currentToken.LiteralBuffer.Length = 0;
                            parsenext3:
                                _currentLocation.Column++;
                                switch (_currentChar = _source.Read()) {
                                    case '`':
                                        _currentChar = _source.Read();
                                        _currentLocation.Column++;
                                        break;
                                    case '\r':
                                        if (_source.Peek() == '\n') {
                                            _source.Read();
                                        }
                                        goto case '\n';
                                    case '\n':
                                        NewLine(currentToken);
                                        currentToken.LiteralBuffer.Append('\n');
                                        goto parsenext3;
                                    case -1:
                                        break;
                                    default:
                                        currentToken.LiteralBuffer.Append((char)_currentChar);
                                        goto parsenext3;
                                }

                                break;

                            case '#':
                                do {
                                    _currentChar = _source.Read();
                                    _currentLocation.Column++;
                                } while (!TeaUnicode.IsLineTerminatorOrEOS(_currentChar));
                                goto skip;

                            case '^':
                                // ^ ^=
                                switch (_currentChar = _source.Read()) {
                                    case '=':
                                        currentToken.Type = TokenType.AssignBitXOr;
                                        _currentChar = _source.Read();
                                        _currentLocation.Column += 2;
                                        break;
                                    default:
                                        currentToken.Type = TokenType.BitXOr;
                                        _currentLocation.Column++;
                                        break;
                                }
                                break;

                            default:
                                if (TeaUnicode.IsWhiteSpace(_currentChar)) {
                                    _currentChar = _source.Read();
                                    _currentLocation.Column++;
                                    goto skip;
                                } else if (TeaUnicode.IsLineTerminator(_currentChar)) {
                                    _currentChar = _source.Read();
                                    NewLine(currentToken);
                                    goto skip;
                                }

                                currentToken.Type = TokenType.Illegal;
                                currentToken.LiteralBuffer.Length = 0;
                                currentToken.LiteralBuffer.AppendFormat("Unexpected Character: {0}", (char)_currentChar);
                                _currentChar = _source.Read();
                                _currentLocation.Column++;
                                break;
                        }

                        #endregion

                    }
                    break;
            }

            currentToken.EndLocation = _currentLocation;

        }

        /// <summary>
        /// 碰到换行符之后重新初始化位置。
        /// </summary>
        void NewLine(Token currentToken) {
            _currentLocation.Line++;
            _currentLocation.Column = 1;
            currentToken.HasLineTerminatorBeforeStart = true;
        }

        int ScanHexEscape(sbyte length, int defaultChar) {

            int ret = 0;

            while (--length >= 0) {
                _currentChar = _source.Read();
                _currentLocation.Column++;
                int value = _currentChar - '0';
                if (value < 0 || value > 9) {
                    value = (_currentChar | 0x20) - 'a';
                    if (value < 0 || value > 5) {
                        // 不合法的字符。
                        return defaultChar;
                    }

                    value += 10;
                }

                ret = (ret << 4) | value;
            }

            return ret;
        }

        int ScanIdentifierUnicodeEscape() {

            if ((_currentChar = _source.Read()) != 'u') {
                return 0;
            }

            return ScanHexEscape(4, 'u');
        }

        void ScanRealNumber(Token currentToken, bool seenPeroid) {

            if (seenPeroid) {

                currentToken.LiteralBuffer.Append((char)'.');

                // 添加当前的内容。
                AddDecimalDigitsToLiteralBuffer(currentToken);

            }

            if (_currentChar == 'e' || _currentChar == 'E') {
                currentToken.LiteralBuffer.Append((char)_currentChar);
                _currentChar = _source.Read();

                if (_currentChar == '+' || _currentChar == '-') {
                    currentToken.LiteralBuffer.Append((char)_currentChar);
                    _currentChar = _source.Read();

                }
                AddDecimalDigitsToLiteralBuffer(currentToken);
            }

            if (_currentChar == 'f' || _currentChar == 'F') {
                currentToken.Type = TokenType.FloatLiteral;
                _currentChar = _source.Read();
                _currentLocation.Column++;
            } else if (_currentChar == 'd' || _currentChar == 'D') {
                currentToken.Type = TokenType.DoubleLiteral;
                _currentChar = _source.Read();
                _currentLocation.Column++;
            } else {
                currentToken.Type = TokenType.DoubleLiteral;
            }
        }

        void AddDecimalDigitsToLiteralBuffer(Token currentToken) {
            do {
                currentToken.LiteralBuffer.Append((char)_currentChar);
                _currentChar = _source.Read();
            } while (TeaUnicode.IsDecimalDigit(_currentChar));
        }

        void ScanIdentifierSuffix(Token currentToken) {

            // 读取一个连续字符。 不考虑这是关键字 或其它符。
            while (TeaUnicode.IsIdentifierPart(_currentChar = _source.Read())) {
                if (_currentChar == '\\') {
                    int c = ScanIdentifierUnicodeEscape();
                    if (!TeaUnicode.IsIdentifierPart(c)) {
                        currentToken.Type = TokenType.Illegal;
                        currentToken.LiteralBuffer.AppendFormat("BadIdentifierPartChar", (char)_currentChar);
                        return;
                    }
                    currentToken.LiteralBuffer.Append((char)_currentChar);
                } else {
                    currentToken.LiteralBuffer.Append((char)_currentChar);
                }
            }

        }

        public string ScanRegularExpressionBody() {

            //  RegularExpressionBody :: See 7.8.5
            //      RegularExpressionFirstChar RegularExpressionChars

            // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags
            bool inCharacterClass = false;

            _currentToken.LiteralBuffer.Length = 0;
            if (_currentToken.Type == TokenType.AssignDiv) {
                _currentToken.LiteralBuffer.Append('=');
            }

            while (_currentChar != '/' || inCharacterClass) {
                if (TeaUnicode.IsLineTerminatorOrEOS(_currentChar))
                    return null;

                if (_currentChar == '\\') {  // Escape sequence.
                    _currentToken.LiteralBuffer.Append('\\');
                    _currentChar = _source.Read();
                    _currentLocation.Column++;
                    if (TeaUnicode.IsLineTerminatorOrEOS(_currentChar))
                        return null;
                    _currentToken.LiteralBuffer.Append((char)_currentChar);
                    _currentChar = _source.Read();
                    _currentLocation.Column++;
                } else {  // Unescaped character.
                    if (_currentChar == '[')
                        inCharacterClass = true;
                    else if (_currentChar == ']')
                        inCharacterClass = false;
                    _currentToken.LiteralBuffer.Append((char)_currentChar);
                    _currentChar = _source.Read();
                    _currentLocation.Column++;
                }
            }
            _currentChar = _source.Read();
            _currentLocation.Column++;

            return _currentToken.LiteralBuffer.ToString();
        }

        /// <summary>
        /// </summary>
        /// <returns>字符串。</returns>
        public string ScanRegularExpressionFlags() {

            //  RegularExpressionFlags ::
            //      [empty]
            //      RegularExpressionFlags IdentifierPart

            _currentToken.LiteralBuffer.Length = 0;

        next:
            if (TeaUnicode.IsLetter(_currentChar)) {
                _currentToken.LiteralBuffer.Append((char)_currentChar);
                _currentChar = _source.Read();
                _currentLocation.Column++;
                goto next;
            }

            string ret = _currentToken.LiteralBuffer.ToString();

            _currentToken.EndLocation = CurrentLocation;

            return ret;
        }

        /// <summary>
        /// </summary>
        /// <returns>字符串。</returns>
        public string ScanWideCharExpression() {

            _currentToken.LiteralBuffer.Length = 0;

            while (_currentChar != '%' && _currentChar >= 0) {
                _currentToken.LiteralBuffer.Append((char)_currentChar);
                _currentChar = _source.Read();
                _currentLocation.Column++;
            }

            string ret = _currentToken.LiteralBuffer.ToString();

            _currentToken.EndLocation = _currentLocation;

            Read();

            return ret;
        }

        #endregion

    }
}
